<?php
namespace hc;
use PDO;
class pdodb{
	private $conn;
	private $lastsql='';//最后执行的sql
	private $rowcount;//返回受影响的行数
	private $tablename;//表名称
	private $sqlattr;//call 函数集合
	private $m_arr=array();//其他方法集合
	private $dbconfig=array();
	static $instance =array();
	//初始化,并连接数据库
	public function __construct($config=array()) {
		$this->dbconfig=$config;
		try {
			if(empty($config)){
				msg_error('mysql 配置文件没有配置');
			}
			$dsn=$config['db_dsn'];
			$root=$config['db_root'];
			$pass=$config['db_pass'];
			//表前缀
			$this->db_prefix=$config['db_prefix'];
			//长连接
			$attr_persistent=$config['db_attr_persistent'];
    		$this->conn= new \PDO($dsn,$root,$pass,array(PDO::ATTR_PERSISTENT=>$attr_persistent));
    		$this->query('set names utf8');
    		$this->sqlattr='where,wherein,order,orderfield,group,limit,join,having,between,notbetween';
    		//缓存表名称到缓存文件

		}catch(PDOException $e){

			msg_error('数据库连接失败：'.$e->getMessage());
		}
	}
	public function __call($name,$args){

		$array = explode(",",$this->sqlattr);
		if(in_array($name,$array)){
			if(!empty($args[0])){

				$this->m_arr[$name]=$args[0];
			}
		}else{
			msg_error('不存在'.$name);
		}
		return $this;
	}
	public function table($table){
		$this->tablename=$this->db_prefix.$table;
	}
	//表前缀
	public function prefix(){
		return $this->db_prefix;
	}
	public function sql_str(){
		if(empty($this->m_arr)){
			$result['sql']='';
			$result['data']=array();
			$result['oldsql']='';
        	return $result;
		}
		$str='';
		$data=array();
		$oldsql='';
		foreach($this->m_arr as $name=>$args){
			if($name=='where'){
				//$where['id']=array('>',1);
				//$where['name']=array('=','你好');
				$str.=" where ";
				$oldsql.=' where ';
				foreach($args as $k=>$v){
					$and_or='&&';
					if(isset($v[2]) && $v[2]=='or'){
						$and_or='||';
					}
					if(isset($v[3]) && $v[3]=='or'){
						$and_or='||';
					}
					if($v[0]=='in'){
						//$v[1]="'".implode("','",$wherein)."'"; 如果是字符串
						//$v[1]=implode(',',$wherein); //是整数
						////$where['id']=array('in',$v1);
						$str.=' '.$k.' in('.$v[1].')'." $and_or ";
						$oldsql.=' '.$k.' in('.$v[1].')'." $and_or ";
					}elseif($v[0]=='notin'){
						$str.=' '.$k.' notin('.$v[1].')'." $and_or ";
						$oldsql.=' '.$k.' notin('.$v[1].')'." $and_or ";
					}elseif($v[0]=='between'){//$where['id']=array('between',100,200);
						$str.=' '.$k.' between '.$this->conn->quote($v[1]).' and '.$this->conn->quote($v[2])." $and_or ";
						$oldsql.=' '.$k.' between '.$this->conn->quote($v[1]).' and '.$this->conn->quote($v[2])." $and_or ";
					}elseif($v[0]=='notbetween'){//$where['id']=array('notbetween',100,200);
						$str.=' '.$k.' not between '.$this->conn->quote($v[1]).' and '.$this->conn->quote($v[2])." $and_or ";
						$oldsql.=' '.$k.' not between '.$this->conn->quote($v[1]).' and '.$this->conn->quote($v[2])." $and_or ";
					}elseif($v[0]=='like'){
						$str.=' '.$k.' like ?'." $and_or";
						$oldsql.=' '.$k.' like '.$this->conn->quote($v[1])." $and_or";
						$data[]=$v[1];
					}elseif($v[0]=='notlike'){
						$str.=''.$k.' like ?'." $and_or";
						$oldsql.=''.$k.' not like '.$this->conn->quote($v[1])." $and_or";
						$data[]=$v[1];
					}else{
						$str.=" ".$k.$v[0]."? $and_or ";
						$data[]=$v[1];
						$oldsql.=" ".$k.$v[0].$this->conn->quote($v[1])." $and_or ";
					}
				}
				$oldsql=substr($oldsql,0,-3);
				$str=substr($str,0,-3);

			}elseif($name=='order'){
				//$str.=' order by ?';
				$str.=' order by '.$args;
				$oldsql.=' order by '.$args;
				//$data[]=$args;
			}elseif($name=='orderfield'){
				//$orderfield='(`log_id`,659,658,660) asc';
				$str.=' order by field'.$args;
				$oldsql.=' order by field'.$args;
			}elseif($name=='group'){
				$str.=" group by ".$args;
				//$str.=' group by ?';
				$oldsql.=' group by '.$args;
				//$data[]=$args;
			}elseif($name=='limit'){
				$str.=" limit ".$args;
				$oldsql.=' limit '.$args;
			}elseif($name=='having'){
				$str.=" having ";
				$oldsql.=" having ";

				foreach($args as $k=>$v){
					$and_or='&&';
					if(isset($v[2]) && $v[2]=='or'){
						$and_or='||';
					}
					if(isset($v[3]) && $v[3]=='or'){
						$and_or='||';
					}
					$str.=" ".$k.$v[0]."? $and_or ";
					$oldsql.=" ".$k.$v[0].$this->conn->quote($v[1])." $and_or ";
					$data[]=$v[1];
				}
				$oldsql.=substr($oldsql,0,-3);
				$str=substr($str,0,-3);
			}elseif($name=='join'){

				//$join=array(array('table','a.id=b.id','left'),array('table','a1.id=b1.id','left'));
				foreach($args as $k=>$v){
					if(isset($v[2])){
						$str.=" ".$v[2]." join ".$this->db_prefix.$v[0]." on ".$v[1];
						$oldsql.=" ".$v[2]." join ".$this->db_prefix.$v[0]." on ".$v[1];
					}else{
						$oldsql.=" inner join ".$this->db_prefix.$v[0]." on ".$v[1];
						$str.=" inner join ".$this->db_prefix.$v[0]." on ".$v[1];
					}
				}

			}
		}
		$this->m_arr = array();//清空联动操作内存
		$result['sql']=$str;
		$result['data']=$data;
		$result['oldsql']=$oldsql;
        return $result;
	}
	//sql 语句执行
	public function query($sql){
		try{
			$this->conn->exec($sql);//没有返回函数
			return true;
        }catch(PDOException $e){
        	$this->lastsql=$sql;
    		msg_error($sql . "<br>" . $e->getMessage());
		}
	}
	//sql 查询
	public function select_query($sql,$array=array()){
		$resource = $this->conn->prepare($sql);
		$resource->setFetchMode(PDO::FETCH_ASSOC);
		if(!$resource->execute($array)){

			msg_error('execute:处理sql语句出错：'.$sql);
		}
		$this->lastsql=$sql;
		return $resource->fetchAll();
	}
	//获取一条数据
	public function select_one($field="*"){
		$sqldata=$this->sql_str();
		$sql = "select ".$field." from ".$this->tablename.$sqldata['sql'];
		//方便打印sql语句
		$this->lastsql='';
		$this->lastsql="select ".$field." from ".$this->tablename.$sqldata['oldsql'];
		$resource = $this->conn->prepare($sql);
		$resource->setFetchMode(PDO::FETCH_ASSOC);
		$sqldata_arr=$sqldata['data'];
		if(!$resource->execute($sqldata_arr)){
			msg_error('execute:处理sql语句出错：'.$this->lastsql);
		}
		return $resource->fetch();
	}

	//查询多条数据
	public function select_all($field="*"){
		$sqldata=$this->sql_str();
		$sql = "select ".$field." from ".$this->tablename.$sqldata['sql'];
		//方便打印sql语句
		$this->lastsql='';
		$this->lastsql="select ".$field." from ".$this->tablename.$sqldata['oldsql'];

		$resource = $this->conn->prepare($sql);
		$resource->setFetchMode(PDO::FETCH_ASSOC);
		if(!$resource->execute($sqldata['data'])){
			msg_error('execute:处理sql语句出错：'.$this->lastsql);
		}
		return $resource->fetchAll();
	}
	/**
	* 插入一条数据
	* @param undefined $data array('id'=>1,'name'=>'name1','age'=>10)
	*
	* @return
	*/
	public function insert_one($data=array()){
		$a3=array();
		$a1=array();
		$a2=array();
		$an=array();
        foreach($data as $key=>$val){
        	$a1[]=$key;
        	$a2[]=$val;//预处理数据
        	$a3[]="?";//设置占位符
        	$an[]="'".$val."'";
        }
       	$sql_end= '('.implode(',',$a1).") values (".implode(',',$a3).')';
		$sql="insert into ".$this->tablename.$sql_end;
		//方便打印sql语句
		$sql_end_v='('.implode(',',$a1).") values (".implode(',',$an).')';
		$this->lastsql="insert into ".$this->tablename.$sql_end_v;
		$resource=$this->conn->prepare($sql);
		if($resource->execute($a2)){
			//受影响的行数
			$this->rowcount=$resource->rowCount();
			//返回最后插入行的ID或序列值
			$lastid=$this->conn->lastInsertId();
			if($lastid){//存在主键
				return $lastid;
			}else{//不存在主键
				return $resource->rowCount();
			}
		}else{
				msg_error('execute:处理sql语句出错：'.$this->lastsql);
		}

	}
	/**
	* 插入多条数据
	* @param undefined $field数组--字段array('id','name','age');
	* @param undefined $data 二维数值  array(array(1,'name1',10),array(2,'name2',20));
	*
	* @return
	*/
	public function insert_all($field=array(),$data=array()){
		$a3=array();
		$a1=array();
		foreach($field as $v){
			$a1[]=$v;
			$a3[]="?";
		}
		//开启事务
		$this->conn->beginTransaction();
		$sql_end= '('.implode(',',$a1).") values (".implode(',',$a3).')';
		$sql="insert into ".$this->tablename.$sql_end;
		$this->lastsql=$sql;
		$resource=$this->conn->prepare($sql);
		foreach($data as $k=>$v){
			$resource->execute($v);
			$rowcount=$resource->rowCount();
			if($rowcount<=0){
				//事务回滚
				$this->conn->rollback();
				msg_error("插入多条数据失败：".$e->getMessage());
			}
		}
		$this->rowcount=$resource->rowCount();
		//提交事务
		$this->conn->commit();
		return $resource->rowCount();
	}

	/**
	*少量添加数据，一条语句添加
	* @param undefined $field array('id','name','age');
	* @param undefined $data
	*
	* @return
	*/
	public function insert_more($field=array(),$data=array()){
		$this->lastsql='';
		$str='';
		$laststr='';
		$count=count($field);
		$sql_data=array();
		foreach($data as $k=>$v){
			$strdata='';
			$laststrdata='';
			for($i=0;$i<$count;$i++){
				$sql_data[]=$v[$field[$i]];
				$strdata.='?,';
				$laststrdata.=$v[$field[$i]].',';
			}
			$strdata=rtrim($strdata,',');
			$laststrdata=rtrim($laststrdata,',');
			$str.='('.$strdata.'),';
			$laststr.='('.$laststrdata.'),';
		}
		$str=rtrim($str,',');
		$laststr=rtrim($laststr,',');
		$sql_end= '('.implode(',',$field).")values".$str;
		$last_sql_end= '('.implode(',',$field).")values".$laststr;
		$sql="insert into ".$this->tablename.$sql_end;
		$this->lastsql="insert into ".$this->tablename.$last_sql_end;
		$resource=$this->conn->prepare($sql);
		if($resource->execute($sql_data)){
			//受影响的行数
			$this->rowcount=$resource->rowCount();
			//返回最后插入行的ID或序列值
			$lastid=$this->conn->lastInsertId();
			if($lastid){//存在主键
				return $lastid;
			}else{//不存在主键
				return $resource->rowCount();
			}
		}else{
			msg_error('execute:处理sql语句出错：'.$this->lastsql);
		}
	}
	/**
	*
	* @param undefined $data
	* @param undefined $IncDec $array('field','field+1')自增与自减功能
	*
	* @return
	*/
	public function update_one($data=array(),$IncDec=array()){
		if(empty($data) && empty($IncDec)){
			msg_error("更新数据不能为空");
		}
		$sqldata=$this->sql_str();
		$sql_show='';
		$a1=array();
		$a2=array();
		$an=array();
		if(!empty($data)){
			foreach ($data as $key=>$val){
              $a1[]=$key."=?";
              $a2[]=$val;
              $an[]=$key."=".$this->conn->quote($val);
       		}
       		$up_set=implode(',',$a1);
		}else{
			$up_set='';
		}
		//方便打印sql语句
		$this->lastsql='';

		if(!empty($IncDec)){
			if($up_set==''){//自增和自减
				$up_set=$IncDec[0].'='.$IncDec[1];
			}else{//自增和自减
				$up_set=$up_set.','.$IncDec[0].'='.$IncDec[1];
				$extendsql=','.$IncDec[0].'='.$IncDec[1];
				$this->lastsql='update '.$this->tablename.' set '.implode(',',$an).$extendsql.$sqldata['oldsql'];
			}
		}else{
			$this->lastsql='update '.$this->tablename.' set '.implode(',',$an).$sqldata['oldsql'];
		}
		$sql='update '.$this->tablename.' set '.$up_set.$sqldata['sql'];
        $resource=$this->conn->prepare($sql);

        if(!empty($sqldata['data'])){
			foreach($sqldata['data'] as $v){
				$a2[]=$v;
			}
		}
        if($resource->execute($a2)){
			return $this->rowcount=$resource->rowCount();
		}else{
			msg_error('execute:处理sql语句出错：'.$this->lastsql);
		}
	}
	//小数据多条更新 条件查询不一样
	/*$updata=array('更改字段'=>array(array('条件语句','条件语句'),'更改值'));
	*/
	/*$updata=array(
		'title'=array(
			array(
				array('id','=',1),
				array('status','=',1),
			),
			'标题1',
		),
		'isshow'=array(
			array(
				array('id','=',1),
				array('status','=',1),
			),
			'1',
		)
	);*/
	//$where['lable']=array('in',$wherein);//批量更新范围重要
	//重点注意 使用范围 一般结合in  加M()->where($where)->update_all_small;不然其他没有修改的值 为null;

	public function update_all_small($updata,$wherein=''){
		$sqldata=$this->sql_str();
		$sql='';
		$this->lastsql='';
		$sql.='update '.$this->tablename." set ";
		$this->lastsql.='update '.$this->tablename." set ";
		$count=count($updata)-1;
		$up_data=array();
		foreach($updata as $k=>$v){
			$sql.=$k.'=case ';
			$this->lastsql.=$k.'=case ';
			foreach($v as $x=>$y){
				$sqlwhere='';//条件语句
				$lastsqlwhere='';
				foreach($y[0] as $j){
					if(isset($j[3])){
						$lastsqlwhere.=$j[0].$j[1].$this->conn->quote($j[2])." $j[3] ";
						$sqlwhere.=$j[0].$j[1].$this->conn->quote($j[2])." $j[3] ";
					}else{
						$lastsqlwhere.=$j[0].$j[1].$this->conn->quote($j[2])." && ";
						$sqlwhere.=$j[0].$j[1].$this->conn->quote($j[2])." && ";
					}
				}
				$sqlwhere=substr($sqlwhere,0,-3);
				$lastsqlwhere=substr($lastsqlwhere,0,-3);
				$sql.='when '.$sqlwhere.' then '."? ";
				$up_data[]=$y[1];
				$this->lastsql.='when '.$lastsqlwhere.' then '.$this->conn->quote($y[1])." ";
			}
			if($count==$k){
				$this->lastsql.='end ';
				$sql.='end ';

			}else{
				$this->lastsql='end,';
				$sql.='end,';
			}
		}
		//where语句
		$sql.=$sqldata['sql'];
		$this->lastsql.=$sqldata['oldsql'];

		if(!empty($sqldata['data'])){
			foreach($sqldata['data'] as $v){
				$up_data[]=$v;
			}
		}
		$resource=$this->conn->prepare($sql);
		if($resource->execute($up_data)){
			return $this->rowcount=$resource->rowCount();
		}else{
			msg_error('execute:处理sql语句出错：'.$this->lastsql);
		}

	}
	//大数据更新
	public function update_all_large(){

	}
	/*
	* 删除数据
	* @param undefined $del_sql
	* @param undefined $data
	*
	* @return
	*/
	public function delete_one(){
		$sqldata=$this->sql_str();
		$sql = 'delete from '.$this->tablename.$sqldata['sql'];
		//方便打印sql语句
		$this->lastsql='';
		$this->lastsql='delete from '.$this->tablename.$sqldata['oldsql'];

		$resource=$this->conn->prepare($sql);
		if(!$resource->execute($sqldata['data'])){
			msg_error('execute:处理sql语句出错：'.$this->lastsql);
		}
		$this->rownum=$resource->rowCount();
		return $resource->rowCount();
	}
	//开启事务
	public function begin(){
		return $this->conn->beginTransaction();
	}
	//事务结束
	public function commit(){
		return $this->conn->commit();
	}
	//事务失败回滚
	public function rollback(){
		return $this->conn->rollback();
	}
	//返回受影响的行数
	public function rows(){
		return $this->rowcount;
	}
	public function last_sql(){
		return $this->lastsql;
	}
	//返回最后一次数据库操作错误信息
	public function enderror(){
		$error=$this->conn->errorinfo();
		return $error;
		//return msg_error('错误代码:'.$error[1].",错误信息:".$error[2]);
	}
	//获取数据库中所有表
	public function tables($db_name=''){
		if(!$tables=$this->cache_file->get('tableslist')){
			if(empty($db_name)){
				$db_name=$this->dbconfig['db_dbname'];
			}
			$sql='show tables from '.$db_name;
			$result=$this->select_query($sql);
			$tables=array();
			foreach($result as $k=>$v){
				$tables[]=$v['Tables_in_'.$db_name];
			}
			$this->cache_file->set('tableslist',$tables,0);
		}
		return $tables;
	}
	//获取表信息
	public function table_info(){
		$sql='show full fields from '.$this->tablename;
		$res=$this->select_query($sql);
		return $res;
	}
	//获取数据库基本信息
	public function mysqlinfo(){
		$data=array();
		//mysql版本
		$res=$this->select_query('select version() as version');
		$data['mysqlversion']=$res[0]['version'];
		return $data;
	}
	//单例模式
	public static function getinstance($config){
		if(!isset(self::$instance[__CLASS__])){
			self::$instance[__CLASS__]=new \hc\pdodb($config);
		}
        return self::$instance[__CLASS__];
    }
}
?>
